#region Copyright
// 
// Copyright (C) 2008 VirtualStaticVoid <virtualstaticvoid@gmail.com>
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
#endregion

using System;
using System.Diagnostics;

namespace NAom.Core
{

  public class PropertyType<TValue> : IPropertyType, IInternalPropertyType
  {

    public PropertyType(string name) :
      this(name, default(TValue))
    {
    }

    public PropertyType(string name, TValue defaultValue)
    {
      if (name == null) throw new ArgumentNullException("name");

      // validations as per PropertyBuilder class
      if (name.Length == 0) throw new ArgumentException(SR.InvalidPropertyTypeName, "name");
      if (name[0] == '\0') throw new ArgumentException(SR.InvalidPropertyTypeName, "name");
      if (name.Length > 0x3ff) throw new ArgumentException(SR.InvalidPropertyTypeName, "name");

      Name = name;
      DataType = typeof(TValue);
      DefaultValue = defaultValue;
    }

    #region IPropertyType Members

    public string Name { get; private set; }

    public Type DataType { get; private set; }

    #endregion

    // NOTE: currently only supported for value types
    // See ms-help://MS.VSCC.v90/MS.msdnexpress.v90.en/fxref_mscorlib/html/548e0f16-3430-d981-d5db-d00a725aae5a.htm for more info
    public TValue DefaultValue { get; set; }

    // NOTE: in this implementation, target is ignored
    protected internal virtual TValue RevertToDefaultValue(object target, TValue value)
    {
      Type typeOfValue = typeof (TValue);

      // what constitutes an unassigned value?
      //  assuming null for reference types and ignored for value types
      // but implementors can override this logic...

      // see http://msdn.microsoft.com/en-us/library/ms366789(VS.80).aspx for details on how to identify nullable types

      if (typeOfValue.IsValueType && !(typeOfValue.IsGenericType && typeOfValue.GetGenericTypeDefinition() == typeof(Nullable<>)))
        return value;

// ReSharper disable CompareNonConstrainedGenericWithNull
      if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
        return this.DefaultValue;
      
      return value;
    }

    internal PropertyAccessor<TTarget, TValue> GetPropertyAccessor<TTarget>()
      where TTarget : class 
    {
      Debug.Assert(InternalPropertyType.IsPropertyAccessorBound, "Property accessor not bound!");
      return (PropertyAccessor<TTarget, TValue>)_propertyAccessor;
    }

    #region IInternalPropertyType Members

    object IInternalPropertyType.DefaultValue
    {
      get { return DefaultValue; }
    }

    //private PropertyAccessor<TTarget, TValue> _propertyAccessor;
    private object _propertyAccessor;

    //void IInternalPropertyType.BindPropertyAccessor<TTarget>(PropertyAccessor<TTarget, TValue> propertyAccessor)
    void IInternalPropertyType.BindPropertyAccessor(object propertyAccessor)
    {
      if (propertyAccessor == null) throw new ArgumentNullException("propertyAccessor");
      _propertyAccessor = propertyAccessor;
    }

    bool IInternalPropertyType.IsPropertyAccessorBound
    {
      get { return _propertyAccessor != null; }
    }

    IPropertyAccessor IInternalPropertyType.PropertyAccessor
    {
      get { return (IPropertyAccessor)_propertyAccessor; }
    }

    object IInternalPropertyType.RevertToDefaultValue(object target, object value)
    {
      return this.RevertToDefaultValue(target, (TValue)value);
    }

    #endregion

    // cast trick
    private IInternalPropertyType InternalPropertyType
    {
      get { return this; }
    }

  }
}
